#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <atomic>
#include "Solution.h"
#include "Object.h"

double Solution::calculateDeltaCost(Instance* inst, int new_x, int new_y) {
    double delta = 0.0;
    auto old_pos = inst->getPosition();
    
    // 遍历实例连接的所有线网
    for (Net* net : inst->getNets()) {
        // 获取线网中所有实例的位置（排除当前实例）
        std::vector<std::pair<int, int>> positions;
        for (Instance* net_inst : net->getInsts()) {
            if (net_inst != inst) {
                positions.push_back(net_inst->getPosition());
            }
        }
        
        // 如果没有其他实例，跳过计算
        if (positions.empty()) continue;
        
        // 计算原边界
        int old_min_x = positions[0].first;
        int old_min_y = positions[0].second;
        int old_max_x = old_min_x;
        int old_max_y = old_min_y;
        
        for (const auto& pos : positions) {
            old_min_x = std::min(old_min_x, pos.first);
            old_min_y = std::min(old_min_y, pos.second);
            old_max_x = std::max(old_max_x, pos.first);
            old_max_y = std::max(old_max_y, pos.second);
        }
        
        // 包含旧位置的边界
        int with_old_min_x = std::min(old_min_x, old_pos.first);
        int with_old_min_y = std::min(old_min_y, old_pos.second);
        int with_old_max_x = std::max(old_max_x, old_pos.first);
        int with_old_max_y = std::max(old_max_y, old_pos.second);
        int hpwl_old = (with_old_max_x - with_old_min_x) + (with_old_max_y - with_old_min_y);
        
        // 包含新位置的边界
        int with_new_min_x = std::min(old_min_x, new_x);
        int with_new_min_y = std::min(old_min_y, new_y);
        int with_new_max_x = std::max(old_max_x, new_x);
        int with_new_max_y = std::max(old_max_y, new_y);
        int hpwl_new = (with_new_max_x - with_new_min_x) + (with_new_max_y - with_new_min_y);
        
        delta += (hpwl_new - hpwl_old);
    }
    return delta;
}
int readBenchMarkFile(std::string i_file_name){

    std::fstream f;
    f.open(i_file_name, std::ios::in);

    if (!f.is_open()){
        std::printf("file %s open failed", i_file_name.c_str());
        return -1;
    }

    std::string line;
    while (std::getline(f, line)){
        if (line.empty())
            break;
        
        std::istringstream iss(line);
        std::string temp;
        std::vector<std::string> row;
        while (iss >> temp){
            row.push_back(temp);
        }
        if (row.size() == 2){
            int l_size_x = std::stoi(row[0]);
            int l_size_y = std::stoi(row[1]);
            glb_fpga.setSize(l_size_x, l_size_y);
            glb_fpga.initialize();
        }else if (row.size() == 3){
            int l_inst_id, l_x, l_y;
            l_inst_id = std::stoi(row[0]);
            l_x = std::stoi(row[1]);
            l_y = std::stoi(row[2]);
            Instance* inst = new Instance(l_x, l_y, l_inst_id, true);
            glb_inst_map[l_inst_id] = inst;
            glb_fpga.addInst(l_x, l_y, inst);
        }else
            break;
    }
    while (std::getline(f, line)){
        if (line.empty())
            continue;
        
        std::istringstream iss(line);
        std::string temp;
        std::vector<std::string> row;
        while (iss >> temp){
            row.push_back(temp);
        }
        int l_inst_id, l_net_id;
        l_inst_id = std::stoi(row[0]);
        Instance* l_inst_point = nullptr;
        if (glb_inst_map.find(l_inst_id) == glb_inst_map.end()){
            l_inst_point = new Instance();
            l_inst_point->setInstId(l_inst_id);
            glb_inst_map[l_inst_id] = l_inst_point;
        }else{
            l_inst_point = glb_inst_map[l_inst_id];
        }
        
        for (size_t i = 1; i < row.size(); i++){
            l_net_id = std::stoi(row[i]);
            Net* lo_net_point = nullptr;
            if (glb_net_map.find(l_net_id) == glb_net_map.end()){
                lo_net_point = new Net;
                lo_net_point->setNetId(l_net_id);
                glb_net_map[l_net_id] = lo_net_point;
            }else{
                lo_net_point = glb_net_map[l_net_id];
            }
            l_inst_point->addNet(lo_net_point);
            lo_net_point->addInst(l_inst_point);
        }
    }
    f.close();
    return 0;
}

int outputSolution(std::string i_file_name){
    std::fstream f;
    f.open(i_file_name, std::ios::out);
    if (!f.is_open()){
        std::printf("unable to open file %s\n", i_file_name.c_str());
        return -1;
    }
    for (size_t i = 0; i < glb_inst_map.size(); i++){
        Instance* lo_inst_p = glb_inst_map[i];
        std::pair<int, int> lo_pos = lo_inst_p->getPosition();
        f << std::setw(5) << std::left << lo_inst_p->getInstId() \
            << std::setw(5) << std::left << lo_pos.first \
            << std::setw(5) << std::left << lo_pos.second << std::endl;
    }
    f.close();
    return 0;
}

int reportWireLength(){
    int l_wirelength = 0;
    for (auto lo_net : glb_net_map){
        l_wirelength += lo_net.second->evalHPWL();
    }
    std::cout << "Wirelength: " << std::setw(5) << std::right << l_wirelength << std::endl;
    return l_wirelength;
}

int reportValid(){

    // 检查布局是否合法
    // 1. 检查每个inst的布局位置是否和Block包含的inst一致
    // 2. 检查每个Block是否存在inst重复出现的情况

    int l_error_count = 0;
    // 先检查每个inst的布局位置是否和Block包含的inst一致
    for (auto lo_inst : glb_inst_map){
        Instance* lo_inst_p = lo_inst.second;
        std::pair<int, int> lo_inst_pos = lo_inst_p->getPosition();
        Block* lo_block_p = glb_fpga.getBlock(lo_inst_pos.first, lo_inst_pos.second);
        if (lo_block_p == nullptr){
            std::printf("[ERROR] inst %d is not placed (%d, %d)\n", lo_inst_p->getInstId(), lo_inst_pos.first, lo_inst_pos.second);
            l_error_count++;
            continue;
        }
        if (lo_block_p->getInsts()[0] != lo_inst_p){
            std::printf("[ERROR] inst %d is not in block (%d, %d)\n", lo_inst_p->getInstId(), lo_inst_pos.first, lo_inst_pos.second);
            l_error_count++;
        }
    }
    // 再从block一侧检查是否存在inst重复出现的情况
    std::set<Instance*> lo_inst_attend;
    for (int i = 0; i < glb_fpga.getSizeX(); i++){
        for (int j = 0; j < glb_fpga.getSizeY(); j++){
            Block* lo_block_p = glb_fpga.getBlock(i, j);
            if (lo_block_p == nullptr)
                continue;
            for (auto lo_inst : lo_block_p->getInsts()){
                if (lo_inst_attend.find(lo_inst) != lo_inst_attend.end()){
                    std::printf("[ERROR] inst %d is repeated in block (%d, %d)\n", lo_inst->getInstId(), i, j);
                    l_error_count++; 
                } 
                lo_inst_attend.insert(lo_inst);
            }
        } 
    }
    return l_error_count;
}
void Solution::placementAnnealing() {
    // 初始化随机数生成器
    std::random_device rd;
    gen = std::mt19937(rd());
    
    // 初始化布局
    initializePlacement();
    double temperature = initialTemperature;
    double currentCost = calculateCost();
    
    // 准备线程私有资源
    std::vector<std::mt19937> thread_gens;
    for (int i = 0; i < num_threads_; ++i) {
        thread_gens.emplace_back(gen() + i); // 确定性种子派生
    }
    
    // 收集所有可移动实例
    std::vector<Instance*> movable_insts;
    for (auto& inst_pair : glb_inst_map) {
        if (!inst_pair.second->isFixed()) {
            movable_insts.push_back(inst_pair.second);
        }
    }
    
    // 按实例ID分组（保证分组确定性）
    std::vector<std::vector<Instance*>> inst_groups(num_threads_);
    for (int i = 0; i < movable_insts.size(); ++i) {
        int tid = movable_insts[i]->getInstId() % num_threads_;
        inst_groups[tid].push_back(movable_insts[i]);
    }
    
    // 主退火循环
    while (temperature > 1.0) {
        for (int i = 0; i < movesPerTemp; i += num_threads_) {
            std::vector<Proposal> proposals(num_threads_);
            
            // 并行生成提议
            #pragma omp parallel for num_threads(num_threads_)
            for (int tid = 0; tid < num_threads_; ++tid) {
                if (inst_groups[tid].empty()) {
                    proposals[tid] = {nullptr, -1, -1, 0.0};
                    continue;
                }
                
                // 线程私有随机操作
                std::uniform_int_distribution<> inst_dist(0, inst_groups[tid].size() - 1);
                Instance* inst = inst_groups[tid][inst_dist(thread_gens[tid])];
                
                std::uniform_int_distribution<> x_dist(0, glb_fpga.getSizeX() - 1);
                std::uniform_int_distribution<> y_dist(0, glb_fpga.getSizeY() - 1);
                
                int new_x = x_dist(thread_gens[tid]);
                int new_y = y_dist(thread_gens[tid]);
                
                // 跳过无效移动
                auto old_pos = inst->getPosition();
                if (new_x == old_pos.first && new_y == old_pos.second) {
                    proposals[tid] = {nullptr, -1, -1, 0.0};
                    continue;
                }
                
                // 计算代价变化
                double delta = calculateDeltaCost(inst, new_x, new_y);
                proposals[tid] = {inst, new_x, new_y, delta};
            }
            
            // 顺序处理提议（保证确定性）
            std::set<Instance*> moved_insts;
            for (int tid = 0; tid < num_threads_; ++tid) {
                Proposal& prop = proposals[tid];
                if (!prop.inst || moved_insts.count(prop.inst)) continue;
                
                // 检查目标位置是否可用
                Block* target_block = glb_fpga.getBlock(prop.new_x, prop.new_y);
                if (!target_block || target_block->getInstsCount() > 0) continue;
                
                // 退火决策
                double acceptanceProb = exp(-prop.delta_cost / temperature);
                std::uniform_real_distribution<> prob_dist(0.0, 1.0);
                
                if (prop.delta_cost > 0 && prob_dist(gen) > acceptanceProb) {
                    continue; // 拒绝
                }
                
                // 执行移动
                moveInstance(prop.inst, prop.new_x, prop.new_y);
                currentCost += prop.delta_cost;
                moved_insts.insert(prop.inst);
            }
        }
        
        // 降温
        temperature *= coolingRate;
    }
}

void Solution::initializePlacement() {
    // Clear all blocks first
    for (int i = 0; i < glb_fpga.getSizeX(); i++) {
        for (int j = 0; j < glb_fpga.getSizeY(); j++) {
            glb_fpga.getBlock(i, j)->clearInst();
        }
    }
    
    // Place fixed instances
    for (auto& inst_pair : glb_inst_map) {
        if (inst_pair.second->isFixed()) {
            auto pos = inst_pair.second->getPosition();
            glb_fpga.addInst(pos.first, pos.second, inst_pair.second);
        }
    }
    
    // Place movable instances randomly
    randomPlacement();
}

void Solution::randomPlacement() {
    // Collect all empty blocks
    std::vector<std::pair<int, int>> emptyBlocks;
    for (int i = 0; i < glb_fpga.getSizeX(); i++) {
        for (int j = 0; j < glb_fpga.getSizeY(); j++) {
            if (glb_fpga.getBlock(i, j)->getInstsCount() == 0) {
                emptyBlocks.emplace_back(i, j);
            }
        }
    }
    
    // Shuffle empty blocks
    std::shuffle(emptyBlocks.begin(), emptyBlocks.end(), gen);
    
    // Place movable instances
    int blockIdx = 0;
    for (auto& inst_pair : glb_inst_map) {
        if (!inst_pair.second->isFixed()) {
            if (blockIdx < emptyBlocks.size()) {
                auto& pos = emptyBlocks[blockIdx++];
                moveInstance(inst_pair.second, pos.first, pos.second);
            } else {
                std::cerr << "Not enough empty blocks for placement!" << std::endl;
                break;
            }
        }
    }
}

void Solution::moveInstance(Instance* inst, int new_x, int new_y) {
    // Remove from old block
    auto old_pos = inst->getPosition();
    Block* oldBlock = glb_fpga.getBlock(old_pos.first, old_pos.second);
    if (oldBlock) {
        oldBlock->clearInst();
    }
    
    // Update instance position
    inst->setPosition(new_x, new_y);
    
    // Add to new block
    Block* newBlock = glb_fpga.getBlock(new_x, new_y);
    if (newBlock) {
        newBlock->addInst(inst);
    }
}

double Solution::calculateTotalHPWL() {
    double total = 0.0;
    for (auto& net_pair : glb_net_map) {
        total += net_pair.second->evalHPWL();
    }
    return total;
}

double Solution::calculateCost() {
    return calculateTotalHPWL();
}

bool Solution::isLegalPlacement() {
    //Check for duplicate placements
    std::set<Instance*> placedInstances;
    for (int i = 0; i < glb_fpga.getSizeX(); i++) {
        for (int j = 0; j < glb_fpga.getSizeY(); j++) {
            Block* block = glb_fpga.getBlock(i, j);
            if (block->getInstsCount() > 0) {
                Instance* inst = block->getInsts()[0];
                if (placedInstances.find(inst) != placedInstances.end()) {
                    return false; // Duplicate placement
                }
                placedInstances.insert(inst);
                
                // Check if instance's position matches block position
                auto pos = inst->getPosition();
                if (pos.first != i || pos.second != j) {
                    return false;
                }
            }
        }
    }
    
    // Check all instances are placed
    for (auto& inst_pair : glb_inst_map) {
        if (placedInstances.find(inst_pair.second) == placedInstances.end()) {
            return false;
        }
    }
    
    return true;
}